#include "TimingOptimizer.h"

#include "api/TimingIDBAdapter.hh"
#include "api/TimingEngine.hh"
#include "liberty/Liberty.hh"

using namespace std;

namespace ieda_contest {

constexpr static float float_equal_tolerance = 1E-15F;
bool fuzzyEqual(float f1, float f2) {
  if (f1 == f2) {
    return true;
  } else if (f1 == 0.0) {
    return abs(f2) < float_equal_tolerance;
  } else if (f2 == 0.0) {
    return abs(f1) < float_equal_tolerance;
  } else {
    return abs(f1 - f2) < 1E-6F * std::max(abs(f1), abs(f2));
  }
}

bool fuzzyLessEqual(float f1, float f2) {
  return f1 < f2 || fuzzyEqual(f1, f2);
}

int TimingOptimizer::_rise = (int)TransType::kRise - 1;
int TimingOptimizer::_fall = (int)TransType::kFall - 1;

TimingOptimizer::TimingOptimizer(DbInterface *dbinterface)
    : _db_interface(dbinterface), _resize_instance_count(0), _inserted_buffer_count(0),
      _insert_instance_index(1), _make_net_index(1) {
  _timing_engine = _db_interface->get_timing_engine();
  _dbu = _db_interface->get_dbu();
  _db_adapter = _timing_engine->get_db_adapter();
  _parasitics_estimator = new EstimateParasitics(_db_interface);
  // _violation_fixer = new ViolationOptimizer(_db_interface);
}

void TimingOptimizer::initBufferCell() {
  // bool not_specified_buffer = _db_interface->get_setup_insert_buffers().empty();
  // if (not_specified_buffer) {
  LibertyCellSeq buf_cells = _db_interface->get_buffer_cells();
  for (auto buf : buf_cells) {
    if (strstr(buf->get_cell_name(), "BUF") != NULL &&
        strstr(buf->get_cell_name(), "CLK") == NULL) {
      _buf_cells.emplace_back(buf);
    }
  }
  return;
  // }

  // auto bufs = _db_interface->get_setup_insert_buffers();
  // for (auto buf : bufs) {
  //   auto buffer = _timing_engine->findLibertyCell(buf.c_str());
  //   _buf_cells.emplace_back(buffer);
  // }
}

void TimingOptimizer::doGateSizing(double time, uint64_t &init_area, uint64_t &max_area) {
  _init_instance_area = init_area;
  _max_instance_area = max_area;
  _total_time += time;
  ieda::Stats gs_status;
  double gs_time_delta = 0;
  // to store slack each time
  vector<Slack> slack_store;

  // _parasitics_estimator->estimateAllNetParasitics();
  // _timing_engine->updateTiming();
  // const char* master_clock_name = "CLK_osc_in";
  // double wns = _timing_engine->get_ista()->getWNS(master_clock_name, AnalysisMode::kMax);
  // double tns = _timing_engine->get_ista()->getTNS(master_clock_name, AnalysisMode::kMax);
  // // TODO calc score
  // std::cout << "wns: " << wns << std::endl;
  // std::cout << "tns: " << tns << std::endl;
  // return;
  // _timing_engine->reportTiming();

  initBufferCell();
  LOG_ERROR_IF(_buf_cells.empty()) << "Can not found specified buffers.\n";

  Slack prev_worst_slack = -kInf;
  int   pass = 1;
  int   decreasing_slack_passes = 0;

  float slack_margin = 0.0;
  // int   _number_passes_allowed_decreasing_slack =
  //     _db_interface->get_number_passes_allowed_decreasing_slack();

  StaSeqPathData *worst_path = worstRequiredPath();
  Slack worst_slack = worst_path->getSlackNs();
  slack_store.push_back(worst_slack);

  auto end_points = getEndPoints();
  // endpoints with setup violation.
  VertexSeq end_pts_setup_violation;
  findEndpointsWithSetupViolation(end_points, slack_margin, end_pts_setup_violation);
  // 根据slack进行升序排序
  sort(end_pts_setup_violation.begin(), end_pts_setup_violation.end(),
       [](StaVertex *end1, StaVertex *end2) {
        return end1->getSlack(AnalysisMode::kMax, TransType::kRise) < end2->getSlack(AnalysisMode::kMax, TransType::kRise);
       });

// 运行太耗时了，稍微修改一下，看效果如何
// auto number = (int)(0.5*end_pts_setup_violation.size());
  int max_cell_num = 30000;
  int excut_cell_num = 0;
  for (auto node : end_pts_setup_violation) {
    gs_time_delta = gs_status.elapsedRunTime();
    // std::cout<<"gs_time_delta" << gs_time_delta <<std::endl;
    if (gs_time_delta + time > 2000) {
      _exit_program = true;
      break;
    }
    doGateSizing(node, worst_slack);
    if (_exit_program) {
      break;
    }
  }
  excut_cell_num = 0;
  if (_exit_program) {
    init_area = _init_instance_area;
    return;
  }
  _parasitics_estimator->excuteParasiticsEstimate();
// 运行太耗时了，稍微修改一下，看效果如何



  // slack violation
  auto number = (int)(end_pts_setup_violation.size());
  // for (auto node : end_pts_setup_violation) {
  // for (int i = number; i < end_pts_setup_violation.size(); i++) {
  for (int i = 0; i < number; i++) {
    excut_cell_num++;
    if (excut_cell_num > max_cell_num) {
      break;
    }
    auto node = end_pts_setup_violation[i];
    prev_worst_slack = -kInf;
    while (worst_slack < slack_margin) {
      gs_time_delta = gs_status.elapsedRunTime();
      if (gs_time_delta + time > 2000) {
        _exit_program = true;
        break;
      }
      doGateSizing(node, worst_slack);
      if (_exit_program) {
        break;
      }
      _parasitics_estimator->excuteParasiticsEstimate();

      // _timing_engine->incrUpdateTiming();

      StaSeqPathData *worst_path_rise = _timing_engine->vertexWorstRequiredPath(
          node, AnalysisMode::kMax, TransType::kRise);
      StaSeqPathData *worst_path_fall = _timing_engine->vertexWorstRequiredPath(
          node, AnalysisMode::kMax, TransType::kFall);
      if (!worst_path_fall || !worst_path_rise) {
        break;
      }
      Slack           worst_slack_rise = worst_path_rise->getSlackNs();
      Slack           worst_slack_fall = worst_path_fall->getSlackNs();
      StaSeqPathData *worst_path =
          worst_slack_rise > worst_slack_fall ? worst_path_fall : worst_path_rise;
      worst_slack = worst_path->getSlackNs();
      slack_store.push_back(worst_slack);

      if (fuzzyLessEqual(worst_slack, prev_worst_slack)) {
        break;
      } else {
        prev_worst_slack = worst_slack;
        decreasing_slack_passes = 0;
      }
    }
    if (_exit_program) {
      break;
    }
    pass++;
  }

  // _parasitics_estimator->estimateAllNetParasitics();
  _timing_engine->updateTiming();

  // wns = _timing_engine->get_ista()->getWNS(master_clock_name, AnalysisMode::kMax);
  // tns = _timing_engine->get_ista()->getTNS(master_clock_name, AnalysisMode::kMax);
  gs_time_delta = gs_status.elapsedRunTime();
  // TODO calc score
  // std::cout << "wns: " << wns << std::endl;
  // std::cout << "tns: " << tns << std::endl;
  // std::cout << "Gate sizing run time " << gs_time_delta << std::endl;
  init_area = _init_instance_area;
  // exit(1);
}

void TimingOptimizer::doGateSizing(StaVertex *vertex, Slack path_slack) {
  StaSeqPathData *worst_path_rise = _timing_engine->vertexWorstRequiredPath(
      vertex, AnalysisMode::kMax, TransType::kRise);
  StaSeqPathData *worst_path_fall = _timing_engine->vertexWorstRequiredPath(
      vertex, AnalysisMode::kMax, TransType::kFall);
  Slack           worst_slack_rise = worst_path_rise->getSlackNs();
  Slack           worst_slack_fall = worst_path_fall->getSlackNs();
  StaSeqPathData *worst_path =
      worst_slack_rise > worst_slack_fall ? worst_path_fall : worst_path_rise;

  vector<TimingEngine::PathNet> path_driver_vertexs =
      _timing_engine->getPathDriverVertexs(worst_path);
  int path_length = path_driver_vertexs.size();

  vector<TimingEngine::PathNet> sorted_path_driver_vertexs;
  for (int i = 0; i < path_length; i++) {
    auto path = path_driver_vertexs[i];
    sorted_path_driver_vertexs.push_back(path);
  }
  sort(sorted_path_driver_vertexs.begin(), sorted_path_driver_vertexs.end(),
       [](TimingEngine::PathNet n1, TimingEngine::PathNet n2) {
         return n1.delay > n2.delay;
       });

  if (path_length > 1) {
    for (int i = 0; i < (int)path_length; i++) {
      auto       path = sorted_path_driver_vertexs[i];
      StaVertex *drvr_vertex = path.driver;
      auto      *obj = drvr_vertex->get_design_obj();
      Pin       *drvr_pin = dynamic_cast<Pin *>(obj);

      // int fanout = getFanoutNumber(drvr_pin);
      // int _rebuffer_max_fanout = _db_interface->get_rebuffer_max_fanout();

      LibertyPort *drvr_port = drvr_pin->get_cell_port();
      float load_cap = drvr_pin->get_net()->getLoad(AnalysisMode::kMax, TransType::kRise);

      vector<TimingEngine::PathNet>::iterator itr =
          find(path_driver_vertexs.begin(), path_driver_vertexs.end(), path);
      int drvr_idx = distance(path_driver_vertexs.begin(), itr);
      if (drvr_idx >= 1) {
        auto         in_path = path_driver_vertexs[drvr_idx - 1];
        StaVertex   *in_vertex = in_path.load;
        auto        *in_obj = in_vertex->get_design_obj();
        Pin         *in_pin = dynamic_cast<Pin *>(in_obj);
        LibertyPort *in_port = in_pin->get_cell_port();

        float        prev_drive;
        auto        *prev_drvr_vertex = in_path.driver;
        auto        *prev_drvr_obj = prev_drvr_vertex->get_design_obj();
        Pin         *prev_drvr_pin = dynamic_cast<Pin *>(prev_drvr_obj);
        LibertyPort *prev_drvr_port = prev_drvr_pin->get_cell_port();
        prev_drive = prev_drvr_port->driveResistance();

        LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap, prev_drive);
        // if (_resize_instance_count >= 100) {
        //   _exit_program=1;
        //   break;
        // }
        if (upsize) {
          TimingIDBAdapter *  idb_adapter = dynamic_cast<TimingIDBAdapter *>(_db_adapter);
          Instance *drvr_inst = drvr_pin->get_own_instance();
          idb::IdbInstance *  dinst = idb_adapter->staToDb(drvr_inst);
          idb::IdbCellMaster *idb_master = dinst->get_cell_master();
          uint64_t init_area = idb_master->get_height() * idb_master->get_width();
          if (repowerInstance(drvr_inst, upsize)) {
            idb::IdbInstance *  dinst_after = idb_adapter->staToDb(drvr_inst);
            idb::IdbCellMaster *idb_master_after = dinst_after->get_cell_master();
            uint64_t replace_area = idb_master_after->get_height() * idb_master_after->get_width();
            auto after_area = _init_instance_area + replace_area - init_area;

      _init_instance_area = after_area;

            _resize_instance_count++;
            _parasitics_estimator->parasiticsInvalid(drvr_pin->get_net());
            // cout << drvr_inst->get_inst_cell()->get_cell_name() << " ---> " << upsize->get_cell_name() << endl;
          }
          break;
        }
      }
    }
  }
}


float TimingOptimizer::calcGateDelay(LibertyPort *drvr_port, float load_cap,
                                    TransType rf) {
  Delay delays[2];
  Slew  slews[2];
  calcGateRiseFallDelays(drvr_port, load_cap, delays, slews);
  int rise_fall = (int)rf - 1;
  return delays[rise_fall];
}

float TimingOptimizer::calcGateDelay(LibertyPort *drvr_port, float load_cap) {
  Delay delays[2];
  Slew  slews[2];
  calcGateRiseFallDelays(drvr_port, load_cap, delays, slews);
  return max(delays[_fall], delays[_rise]);
}

int TimingOptimizer::getFanoutNumber(Pin *pin) {
  auto *net = pin->get_net();
  return net->getFanouts();
}

bool TimingOptimizer::hasTopLevelOutputPort(Net *net) {
  DesignObject *pin;
  FOREACH_NET_PIN(net, pin) {
    if (pin->isOutput()) {
      return true;
    }
  }
  return false;
}


LibertyCell *TimingOptimizer::upsizeCell(LibertyPort *in_port, LibertyPort *drvr_port,
                                        float load_cap, float prev_drive) {
  LibertyCell           *cell = drvr_port->get_ower_cell();
  auto name = cell->get_cell_name();
  // cout << name << endl;
  Vector<LibertyCell *> *equiv_cells = _timing_engine->equivCells(cell);
  if (equiv_cells) {
  // cout << "equiv_cells size -> " << equiv_cells->size() << endl;
    const char *in_port_name = in_port->get_port_name();
    const char *drvr_port_name = drvr_port->get_port_name();

    sort(equiv_cells->begin(), equiv_cells->end(),
         [=](LibertyCell *cell1, LibertyCell *cell2) {
           LibertyPort *port1 = cell1->get_cell_port_or_port_bus(drvr_port_name);
           LibertyPort *port2 = cell2->get_cell_port_or_port_bus(drvr_port_name);
           return (port1->driveResistance() > port2->driveResistance());
         });

    // float drive = drvr_port->driveResistance();
    float delay =
        calcGateDelay(drvr_port, load_cap) + prev_drive * in_port->get_port_cap();

    for (LibertyCell *equiv : *equiv_cells) {
      if (strstr(equiv->get_cell_name(), "CLK") != NULL) {
        continue;
      }

      if (strcmp(equiv->get_cell_name(), cell->get_cell_name()) == 0) {
        continue;
      }
      LibertyPort *equiv_drvr = equiv->get_cell_port_or_port_bus(drvr_port_name);
      LibertyPort *equiv_input = equiv->get_cell_port_or_port_bus(in_port_name);

      // Include delay of previous driver into equiv gate.
      float equiv_delay;
      if (equiv_input) {
        equiv_delay = calcGateDelay(equiv_drvr, load_cap) + prev_drive * equiv_input->get_port_cap();
      } else {
        equiv_delay =
            calcGateDelay(equiv_drvr, load_cap) + prev_drive * in_port->get_port_cap();
      }

      if (equiv_delay < delay) {
        return equiv;
      }
    }
  }
  return nullptr;
}

StaSeqPathData *TimingOptimizer::worstRequiredPath() {
  vector<TransType> rise_fall = {TransType::kRise, TransType::kFall};
  StaSeqPathData *worst_path = nullptr;
  Slack wns = kInf;
  for (auto rf : rise_fall) {
    auto path = _timing_engine->vertexWorstRequiredPath(AnalysisMode::kMax, rf);
    if (path->getSlackNs() < wns) {
      wns = path->getSlackNs();
      worst_path = path;
    }
  }
  return worst_path;
}

bool TimingOptimizer::netConnectToPort(Net *net) {
  auto load_pin_ports = net->getLoads();
  for (auto pin_port : load_pin_ports) {
    if (pin_port->isPort()) {
      return true;
    }
  }
  return false;
}

Slack TimingOptimizer::getWorstSlack(StaVertex *vertex, AnalysisMode mode) {
  auto  rise_slack = vertex->getSlackNs(mode, TransType::kRise);
  Slack rise = rise_slack ? *rise_slack : kInf;
  auto  fall_slack = vertex->getSlackNs(mode, TransType::kFall);
  Slack fall = fall_slack ? *fall_slack : kInf;
  Slack slack = min(rise, fall);
  return slack;
}

VertexSet TimingOptimizer::getEndPoints() {
  VertexSet  end_points;
  auto      *ista = _timing_engine->get_ista();
  StaGraph  *the_graph = &(ista->get_graph());
  StaVertex *vertex;
  FOREACH_END_VERTEX(the_graph, vertex) { end_points.insert(vertex); }
  return end_points;
}

void TimingOptimizer::findEndpointsWithSetupViolation(VertexSet end_points, Slack slack_margin,
                                                   // return values
                                                   VertexSeq &setup_violations) {
  setup_violations.clear();

  for (auto *end : end_points) {
    Slack slack = getWorstSlack(end, AnalysisMode::kMax);
    if (slack < slack_margin) {
      setup_violations.emplace_back(end);
    }
  }
}


bool TimingOptimizer::repowerInstance(Pin *drvr_pin) {
  Instance *   inst = drvr_pin->get_own_instance();
  LibertyCell *cell = inst->get_inst_cell();
  if (cell) {
    Vector<LibertyCell *> *equiv_cells = _timing_engine->equivCells(cell);
    if (equiv_cells) {
      bool revisiting_inst = false;
      // if (hasMultipleOutputs(inst)) {
      //   if (_resized_multi_output_insts.find(inst) != _resized_multi_output_insts.end()) {
      //     revisiting_inst = true;
      //   }
      //   _resized_multi_output_insts.insert(inst);
      // }
      bool is_buf_inv = cell->isBuffer() || cell->isInverter();

      Net *net = drvr_pin->get_net();
      if (net) {
        _parasitics_estimator->estimateInvalidNetParasitics(drvr_pin, net);
      }

      // Includes net parasitic capacitance.
      double load = drvr_pin->get_net()->getLoad(AnalysisMode::kMax, TransType::kRise);
      if (load > 0.0) {
        LibertyCell *best_cell = cell;
        float        target_load = (*_db_interface->get_cell_target_load_map())[cell];
        float        best_load = target_load;
        float        best_dist = abs(load - target_load); //
        float        best_delay = is_buf_inv ? calcBufferDelay(cell, load) : 0.0;

        for (auto *target_cell : *equiv_cells) {
          //!_db_interface->dontUse(target_cell) // TODO:

          // Do not use clk buffer in signal net, and vice versa
          const char *buf_name = target_cell->get_cell_name();
          if (strstr(buf_name, "CLK") != NULL || strstr(buf_name, "24") != NULL ||
              strstr(buf_name, "20") != NULL) {
            continue;
          }

          if (_db_interface->isLinkCell(target_cell)) {
            float target_cell_load =
                (*_db_interface->get_cell_target_load_map())[target_cell];
            float delay = is_buf_inv ? calcBufferDelay(target_cell, load) : 0.0;
            float dist = abs(load - target_cell_load);

            if (is_buf_inv
                    ? ((delay < best_delay && dist < best_dist * 1.1) ||
                       (dist < best_dist && delay < best_delay * 1.1))
                    : dist < best_dist && (!revisiting_inst || target_load > best_load)) {
              best_cell = target_cell;
              best_dist = dist;
              best_load = target_load;
              best_delay = delay;
            }
          }
        }
        if (best_cell != cell) {
          return repowerInstance(inst, best_cell) && !revisiting_inst;
        }
      }
    }
  }
  return false;
}

bool TimingOptimizer::repowerInstance(Instance *inst, LibertyCell *replace) {
  const char *replacement_name = replace->get_cell_name();

  TimingIDBAdapter *  idb_adapter = dynamic_cast<TimingIDBAdapter *>(_db_adapter);
  idb::IdbLayout *    layout = idb_adapter->get_idb()->get_def_service()->get_layout();
  idb::IdbCellMaster *replacement_master =
      layout->get_cell_master_list()->find_cell_master(replacement_name);
  if (replacement_master) {
    idb::IdbInstance *  dinst = idb_adapter->staToDb(inst);
    idb::IdbCellMaster *idb_master = dinst->get_cell_master();
    // Master *            master = new Master(idb_master);
    // float               area_master = DesignCalculator::calcMasterArea(master, _dbu);

    if (replacement_master->get_name() == idb_master->get_name()) {
      return false;
    }

    // increDesignArea(-area_master);

    idb::IdbCellMaster *replace_master_idb = idb_adapter->staToDb(replace);

    uint64_t init_area = idb_master->get_height() * idb_master->get_width();
    uint64_t replace_area = replacement_master->get_height() * replacement_master->get_width();
    // uint64_t delta_area = replace_area - init_area;
    uint64_t after_area = _init_instance_area + replace_area - init_area;
    if (after_area > _max_instance_area) {
      cout << "======================_init_instance_area  " << _init_instance_area << endl;
      cout << "======================_max_instance_area  " << _max_instance_area << endl;
      cout << "max area achieve \n";
      _exit_program = true;
      return false;
    }

    idb_adapter->replaceCell(inst, replace);
    _timing_engine->repowerInstance(inst->get_name(), replace->get_cell_name());
    // increDesignArea(area_replace_master);

    // _init_instance_area += delta_area;
    return true;
  }
  return false;
}

void TimingOptimizer::doDownSize(uint64_t& init_area, uint64_t& max_area)
{
  _init_instance_area = init_area;
  _max_instance_area = max_area;
  auto setup_violation_end_pts = findViolationNode();
  for (auto node : setup_violation_end_pts) {
    downSize(node);
    if (_exit_program) {
      init_area = _init_instance_area;
      return;
    }
  }
  init_area = _init_instance_area;
}

void TimingOptimizer::downSize(StaVertex *vertex)
{
    StaSeqPathData *worst_path_rise = _timing_engine->vertexWorstRequiredPath(
        vertex, AnalysisMode::kMax, TransType::kRise);
    StaSeqPathData *worst_path_fall = _timing_engine->vertexWorstRequiredPath(
        vertex, AnalysisMode::kMax, TransType::kFall);
    Slack worst_slack_rise = worst_path_rise->getSlackNs();
    Slack worst_slack_fall = worst_path_fall->getSlackNs();
    StaSeqPathData *worst_path =
        worst_slack_rise > worst_slack_fall ? worst_path_fall : worst_path_rise;

#ifndef MULTIPATH
    vector<TimingEngine::PathNet> timing_path =
        _timing_engine->getPathDriverVertexs(worst_path);
    int path_length = timing_path.size();

    for (int i = 0; i < path_length; i++)
    {
        auto path_net = timing_path[i];
        auto net = path_net.driver->get_design_obj()->get_net();

        auto drvr_pin = net->getDriver();
        if (!drvr_pin->isPin())
        {
            continue;
        }
        // auto drvr_inst = dynamic_cast<ista::Pin *>(drvr_pin)->get_own_instance();

        auto loads = net->getLoads();
        for (auto load : loads)
        {
            if (!load->isPin())
            {
                continue;
            }
            // 负载单元的时序信息
            auto load_pin = dynamic_cast<ista::Pin *>(load);
            auto *the_vertex = _timing_engine->findVertex(load_pin->getFullName().c_str());
            Slack load_pin_slack = getWorstSlack(the_vertex, AnalysisMode::kMax);
            if (load_pin_slack > 0.0)
            {
                // downsize
                auto load_cell = load_pin->get_cell_port()->get_ower_cell();
                Vector<LibertyCell *> *equiv_cells = _timing_engine->equivCells(load_cell);
                if (equiv_cells)
                {
                    // std::cout << "equiv_cells size -> " << equiv_cells->size() << std::endl;
                    const char *drvr_port_name = load_pin->get_cell_port()->get_port_name();

                    std::sort(equiv_cells->begin(), equiv_cells->end(),
                                [=](LibertyCell *cell1, LibertyCell *cell2)
                                {
                                    LibertyPort *port1 = cell1->get_cell_port_or_port_bus(drvr_port_name);
                                    LibertyPort *port2 = cell2->get_cell_port_or_port_bus(drvr_port_name);
                                    // return (port1->driveResistance() > port2->driveResistance());
                                    return (port1->get_port_cap() < port2->get_port_cap());
                                });
                    repowerInstance(load_pin->get_own_instance(), equiv_cells->front());
                    if (_exit_program) {
                      return;
                    }
                }
            }
            // auto load_inst = load_pin->get_own_instance();
            // auto load_cell = load_pin->get_own_cell();
            // auto load_lib_cell = load_cell->getLibertyCell();
            // auto load_cell_area = load_lib_cell->getArea();
            // auto load_cell_pin = load_lib_cell->getPin("A");
            // auto load_cell_pin_cap = load_cell_pin->getCapacitance();

            // auto load_inst = dynamic_cast<ista::Pin *>(load)->get_own_instance();
        }
    }
#else
    vector<TimingEngine::PathNet> timing_path = _timing_engine->getPathDriverVertexs(worst_path);
    vector<ista::StaArc *> arcs = timing_path[0].driver->get_snk_arcs();

    ista::StaVertex *_clk_start_vertex = nullptr;
    for (auto arc : arcs)
    {
        if (arc->get_src()->is_clock())
        {
            _clk_start_vertex = arc->get_src();
            break;
        }
    }
    auto worst_paths = _timing_engine->getViolatedSeqPathsBetweenTwoSinks(
        timing_path[timing_path.size() - 1].load->getName().c_str(),
        _clk_start_vertex->getName().c_str(),
        AnalysisMode::kMax);
    // int max_path_read = 4;
    // int b = 0;
    // while (!worst_paths.empty() && b < max_path_read) {
    while (!worst_paths.empty())
    {
        // for (auto worst_path : worst_paths)
        // {
        vector<TimingEngine::PathNet> timing_path =
            _timing_engine->getPathDriverVertexs(worst_paths.top());
        worst_paths.pop();
        int path_length = timing_path.size();

        for (int i = 0; i < path_length; i++)
        {
            auto path_net = timing_path[i];
            auto net = path_net.driver->get_design_obj()->get_net();

            auto drvr_pin = net->getDriver();
            if (!drvr_pin->isPin())
            {
                continue;
            }
            // auto drvr_inst = dynamic_cast<ista::Pin *>(drvr_pin)->get_own_instance();

            auto loads = net->getLoads();
            for (auto load : loads)
            {
                if (!load->isPin())
                {
                    continue;
                }
                // 负载单元的时序信息
                auto load_pin = dynamic_cast<ista::Pin *>(load);
                auto *the_vertex = _timing_engine->findVertex(load_pin->getFullName().c_str());
                Slack load_pin_slack = getWorstSlack(the_vertex, AnalysisMode::kMax);
                if (load_pin_slack > 0)
                {
                    // downsize
                    auto load_cell = load_pin->get_cell_port()->get_ower_cell();
                    Vector<LibertyCell *> *equiv_cells = _timing_engine->equivCells(load_cell);
                    if (equiv_cells)
                    {
                        // std::cout << "equiv_cells size -> " << equiv_cells->size() << std::endl;
                        const char *drvr_port_name = load_pin->get_cell_port()->get_port_name();

                        std::sort(equiv_cells->begin(), equiv_cells->end(),
                                    [=](LibertyCell *cell1, LibertyCell *cell2)
                                    {
                                        LibertyPort *port1 = cell1->get_cell_port_or_port_bus(drvr_port_name);
                                        LibertyPort *port2 = cell2->get_cell_port_or_port_bus(drvr_port_name);
                                        // return (port1->driveResistance() > port2->driveResistance());
                                        return (port1->get_port_cap() < port2->get_port_cap());
                                    });
                        repowerInstance(load_pin->get_own_instance(), equiv_cells->front());
                    }
                }
            }
        }
        // }
    }
#endif
}

VertexSeq TimingOptimizer::findViolationNode()
{
    VertexSet end_points;
    VertexSeq setup_violations;
    auto *ista = _timing_engine->get_ista();
    StaGraph *the_graph = &(ista->get_graph());
    StaVertex *vertex;
    FOREACH_END_VERTEX(the_graph, vertex) { end_points.insert(vertex); }
    for (auto *end : end_points)
    {
        Slack slack = getWorstSlack(end, AnalysisMode::kMax);
        if (slack < 0.0)
        {
            setup_violations.emplace_back(end);
        }
    }
    return setup_violations;
}

} // namespace ieda_contest